package me.zhengjie.nettydemo.nettty;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import me.zhengjie.pojo.entity.Lift;
import me.zhengjie.nettydemo.mapper.LiftNettyMapper;
import me.zhengjie.sqlservilce.LiftSqlService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * @author : huang
 * @since : 2024/5/9 9:19
 */

//@Slf4j
@Data
@RequiredArgsConstructor
public class LiftClient {

    private final String ip;
    private final int port;
    private final String liftCode;
    private final LiftNettyMapper liftMapper;
    private Channel channel;
    private EventLoopGroup group;
    private static final Logger liftLog = LoggerFactory.getLogger("lift");
    private final LiftSqlService liftSqlService;

    public void disconnect() {
        if (this.channel != null) {
            liftLog.info("连接通道不为null");
            this.channel.disconnect();
            try {
                this.channel.closeFuture().sync();
            } catch (Exception e) {
                liftLog.error("关闭通道时发生异常", e);
            } finally {
                if (this.group != null) {
                    liftLog.info("group不为null");
                    this.group.shutdownGracefully();
                    try {
                        this.group.awaitTermination(5, TimeUnit.SECONDS);
                    } catch (InterruptedException e) {
                        liftLog.error("等待线程池关闭时发生异常", e);
                    }
                }
            }
        }
    }

    public Channel getChannel() {
        return this.channel;
    }

    public boolean connect() {
        Bootstrap client = new Bootstrap();
        client.option(ChannelOption.SO_KEEPALIVE, true);

        group = new NioEventLoopGroup();
        client.group(group);

        client.channel(NioSocketChannel.class);
        client.handler(new ChannelInitializer<NioSocketChannel>() {
            @Override
            protected void initChannel(NioSocketChannel ch) throws Exception {

//                    ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(
//                            1024, 1, 1, -1, 0));
                ch.pipeline().addLast(new MultiProtocolDecoder());
                ch.pipeline().addLast(new SimpleClientHandler(liftMapper, liftSqlService,liftCode));
            }
        });

        try {
            liftLog.info("连接ip：{},端口",ip,port);
            ChannelFuture future = client.connect(this.ip, this.port).sync();
            boolean success = future.isSuccess();
            if (success) {
                this.channel = future.channel();
                liftLog.info("连接成功，通道: {}", this.channel);
            }
            return true;
        } catch (Exception e) {
            liftLog.warn("IP：{}，PORT：{}，连接失败", this.ip, this.port);
            return false;
        }
    }

    // -1 代表接收失败
    public synchronized CompletableFuture<Integer> callLift(int floor) {
        //更具电梯查询，获取区域，判断新电梯还是旧电梯
        Lift lift = liftSqlService.selectLiftCode(liftCode);
        ByteBuf buf = Unpooled.buffer(8);
        if (lift !=null){
            Integer liftRegion = lift.getLiftRegion();
            if (liftRegion ==1){
                buf.writeByte(0x01);
                buf.writeByte(0x06);
                buf.writeByte(0x9C);
                buf.writeByte(0x99);
                buf.writeByte(0x00);
                //新电梯
                switch (floor){
                    case 1:
                        buf.writeByte(0X01);
                        buf.writeByte(0XB6);
                        buf.writeByte(0x75);
                        break;
                    case 2:
                        buf.writeByte(0X02);
                        buf.writeByte(0XF6);
                        buf.writeByte(0x74);
                        break;
                    case 3:
                        buf.writeByte(0X04);
                        buf.writeByte(0X76);
                        buf.writeByte(0x76);
                        break;
                    case 4:
                        buf.writeByte(0X08);
                        buf.writeByte(0X76);
                        buf.writeByte(0x73);
                        break;
                    case 5:
                        buf.writeByte(0X10);
                        buf.writeByte(0X76);
                        buf.writeByte(0x79);
                        break;
                }
            }else {
                //旧电梯

            }
        }
        if (this.channel == null)
            return CompletableFuture.completedFuture(-1);
        liftLog.info("呼叫电梯信息发送记录：{},电梯号:{},发送指令时间:{}", ByteBufUtil.hexDump(buf),liftCode,LocalDateTime.now());
        ChannelFuture channelFuture = this.channel.writeAndFlush(buf);
        try {
            Thread.sleep(LiftLock.sleep);
            liftLog.info("呼叫电梯后睡眠10毫秒时间:{},电梯号:{}", LocalDateTime.now(),liftCode);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return commonHandle(channelFuture, 0x00);

    }

    public synchronized CompletableFuture<Integer> closeDoorAndGoToFloor(int TargetFloor) {
        Lift lift = liftSqlService.selectLiftCode(liftCode);
        ByteBuf buf = Unpooled.buffer(10);
        if (lift !=null){
            Integer liftRegion = lift.getLiftRegion();
            if (liftRegion ==1){
                buf.writeByte(0xaa);
                buf.writeByte(0x09);
                buf.writeByte(0x00);
                buf.writeByte(0x00);
                buf.writeByte(0x22);
                buf.writeByte(TargetFloor);
                buf.writeByte(0x00);
                buf.writeByte(0x01);
                int crc = TargetFloor + 0x2E;
                buf.writeByte(crc);
                buf.writeByte(0xbb);
            }else {
                buf.writeByte(0xaa);
                buf.writeByte(0x09);
                buf.writeByte(0x00);
                buf.writeByte(0x00);
                buf.writeByte(0x22);
                buf.writeByte(TargetFloor);
                buf.writeByte(0x00);
                buf.writeByte(0x01);
                int crc = TargetFloor + 0x2E;
                buf.writeByte(crc);
                buf.writeByte(0xbb);
            }
        }

        if (this.channel == null)
            return CompletableFuture.completedFuture(-1);

        liftLog.info("关门至某层信息发送记录：{},电梯号:{},发送指令时间:{}", ByteBufUtil.hexDump(buf),liftCode,LocalDateTime.now());
        ChannelFuture channelFuture = this.channel.writeAndFlush(buf);
        try {
            Thread.sleep(LiftLock.sleep);
            liftLog.info("关门至某层睡眠10毫秒时间:{},电梯号:{}",LocalDateTime.now(),liftCode);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return commonHandle(channelFuture, 0x00);
    }

    public synchronized CompletableFuture<Integer> closeDoor() {
        Lift lift = liftSqlService.selectLiftCode(liftCode);
        ByteBuf buf = Unpooled.buffer(8);
        if (lift !=null){
            Integer liftRegion = lift.getLiftRegion();
            if (liftRegion == 1){
                buf.writeByte(0x01);
                buf.writeByte(0x06);
                buf.writeByte(0x9C);
                buf.writeByte(0x56);
                buf.writeByte(0x00);
                buf.writeByte(0x04);
                buf.writeByte(0x46);
                buf.writeByte(0x49);
            }else {
                buf.writeByte(0xaa);
                buf.writeByte(0x09);
                buf.writeByte(0x00);
                buf.writeByte(0x00);
                buf.writeByte(0x25);
                buf.writeByte(0x00);
                buf.writeByte(0x00);
                buf.writeByte(0x01);
                buf.writeByte(0x32);
                buf.writeByte(0xbb);
            }
        }

        if (this.channel == null)
            return CompletableFuture.completedFuture(-1);

        liftLog.info("关门信息发送记录：{},电梯号:{},发送指令时间:{}", ByteBufUtil.hexDump(buf),liftCode,LocalDateTime.now());
        ChannelFuture channelFuture = this.channel.writeAndFlush(buf);
        try {
            Thread.sleep(LiftLock.sleep);
            liftLog.info("关门后睡眠10毫秒时间:{},电梯号:{}",LocalDateTime.now(),liftCode);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return commonHandle(channelFuture, 0x00);
    }


    public synchronized CompletableFuture<Integer> openDoor() {
        Lift lift = liftSqlService.selectLiftCode(liftCode);
        ByteBuf buf = Unpooled.buffer(8);
        if (lift !=null){
            Integer liftRegion = lift.getLiftRegion();
            if (liftRegion == 1){
                buf.writeByte(0x01);
                buf.writeByte(0x06);
                buf.writeByte(0x9C);
                buf.writeByte(0x56);
                buf.writeByte(0x00);
                buf.writeByte(0x03);
                buf.writeByte(0x07);
                buf.writeByte(0x8B);
            }else {
                buf.writeByte(0xaa);
                buf.writeByte(0x09);
                buf.writeByte(0x00);
                buf.writeByte(0x00);
                buf.writeByte(0x25);
                buf.writeByte(0x00);
                buf.writeByte(0x00);
                buf.writeByte(0x01);
                buf.writeByte(0x32);
                buf.writeByte(0xbb);
            }
        }

        if (this.channel == null)
            return CompletableFuture.completedFuture(-1);

        liftLog.info("关门信息发送记录：{},电梯号:{},发送指令时间:{}", ByteBufUtil.hexDump(buf),liftCode,LocalDateTime.now());
        ChannelFuture channelFuture = this.channel.writeAndFlush(buf);
        try {
            Thread.sleep(LiftLock.sleep);
            liftLog.info("关门后睡眠10毫秒时间:{},电梯号:{}",LocalDateTime.now(),liftCode);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return commonHandle(channelFuture, 0x00);
    }

    public synchronized void maintainAGVMode() {
        ByteBuf buf = Unpooled.buffer(8);
        Lift lift = liftSqlService.selectLiftCode(liftCode);
        if (lift !=null){
            Integer liftRegion = lift.getLiftRegion();
            if (liftRegion == 1){
                buf.writeByte(0x01);
                buf.writeByte(0x06);
                buf.writeByte(0x9C);
                buf.writeByte(0xA4);
                buf.writeByte(0x00);
                buf.writeByte(0x00);
                buf.writeByte(0xB6);
                buf.writeByte(0x79);
            }
        }
        liftLog.info("打印通道channel：{}",channel);
        if (this.channel == null)
            return;
        liftLog.info("电梯号:{}控制电梯保持AGV模式发送记录：{},发送指令时间:{}",liftCode, ByteBufUtil.hexDump(buf),LocalDateTime.now());
        this.channel.writeAndFlush(buf);
        try {
            Thread.sleep(LiftLock.sleep);
            liftLog.info("查询电梯睡眠10毫秒时间:{},电梯号:{}",LocalDateTime.now(),liftCode);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void liftInfoHeartbeat() {
        ByteBuf buf = Unpooled.buffer(8);
        Lift lift = liftSqlService.selectLiftCode(liftCode);
        if (lift !=null){
            Integer liftRegion = lift.getLiftRegion();
            if (liftRegion == 1){
                buf.writeByte(0x01);
                buf.writeByte(0x03);
                buf.writeByte(0x9C);
                buf.writeByte(0x41);
                buf.writeByte(0x00);
                buf.writeByte(0x05);
                buf.writeByte(0xFB);
                buf.writeByte(0x8D);
            }else {
                //旧电梯查询
            }
        }
        liftLog.info("打印通道channel：{}",channel);
        if (this.channel == null)
            return;
        liftLog.info("电梯号:{}查询电梯状态心跳信息发送记录：{},发送指令时间:{}",liftCode, ByteBufUtil.hexDump(buf),LocalDateTime.now());
        this.channel.writeAndFlush(buf);
        try {
            Thread.sleep(LiftLock.sleep);
            liftLog.info("查询电梯睡眠10毫秒时间:{},电梯号:{}",LocalDateTime.now(),liftCode);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //todo 查询电梯输入
    public synchronized CompletableFuture<Integer> liftStatusFrame() {
        ByteBuf buf = Unpooled.buffer(7);
        buf.writeByte(0xaa);
        buf.writeByte(0x06);
        buf.writeByte(0xcc);
        buf.writeByte(0x00);
        buf.writeByte(0x1A);
        buf.writeByte(0x01);
        buf.writeByte(0xbb);
        if (this.channel == null)
            return CompletableFuture.completedFuture(-1);
//        log.info("查询电梯输入发送记录：{},电梯号:{},发送指令时间:{}", ByteBufUtil.hexDump(buf),liftCode,LocalDateTime.now());
        ChannelFuture channelFuture = this.channel.writeAndFlush(buf);
        try {
            Thread.sleep(LiftLock.sleep);
//            log.info("查询电梯输入睡眠10毫秒时间:{},电梯号:{}", LocalDateTime.now(),liftCode);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return commonHandle(channelFuture, 0x00);
    }

    //todo 查询电梯输出
    public synchronized CompletableFuture<Integer> liftStatusOutput() {
        ByteBuf buf = Unpooled.buffer(7);
        buf.writeByte(0xaa);
        buf.writeByte(0x06);
        buf.writeByte(0xcc);
        buf.writeByte(0x00);
        buf.writeByte(0x1B);
        buf.writeByte(0x01);
        buf.writeByte(0xbb);
        if (this.channel == null)
            return CompletableFuture.completedFuture(-1);
//        log.info("查询电梯输出发送记录：{},电梯号:{},发送指令时间:{}", ByteBufUtil.hexDump(buf),liftCode,LocalDateTime.now());
        ChannelFuture channelFuture = this.channel.writeAndFlush(buf);
        try {
            Thread.sleep(LiftLock.sleep);
//            log.info("查询电梯输出睡眠10毫秒时间:{},电梯号:{}", LocalDateTime.now(),liftCode);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return commonHandle(channelFuture, 0x00);
    }

    //todo 控制继电器电梯输出打开，开启agv模式
    public synchronized CompletableFuture<Integer> controlLiftStatusOutputOpenIt() {
        ByteBuf buf = Unpooled.buffer(9);
        buf.writeByte(0xaa);
        buf.writeByte(0x08);
        buf.writeByte(0xcc);
        buf.writeByte(0x00);
        buf.writeByte(0x1C);
        buf.writeByte(0x02);
        buf.writeByte(0x01);
        buf.writeByte(0x01);
        buf.writeByte(0xbb);
        if (this.channel == null)
            return CompletableFuture.completedFuture(-1);
        liftLog.info("控制继电器电梯输出打开，开启agv模式发送记录：{},电梯号:{},发送指令时间:{}", ByteBufUtil.hexDump(buf),liftCode,LocalDateTime.now());
        ChannelFuture channelFuture = this.channel.writeAndFlush(buf);
        try {
            Thread.sleep(LiftLock.sleep);
            liftLog.info("控制继电器电梯输出打开，开启agv模式睡眠10毫秒时间:{},电梯号:{}", LocalDateTime.now(),liftCode);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return commonHandle(channelFuture, 0x00);
    }
    //todo 控制继电器电梯输出关闭，开启手动模式
    public synchronized CompletableFuture<Integer> controlLiftStatusOutputShutDown() {
        ByteBuf buf = Unpooled.buffer(9);
        buf.writeByte(0xaa);
        buf.writeByte(0x08);
        buf.writeByte(0xcc);
        buf.writeByte(0x00);
        buf.writeByte(0x1C);
        buf.writeByte(0x02);
        buf.writeByte(0x00);
        buf.writeByte(0x00);
        buf.writeByte(0xbb);
        if (this.channel == null)
            return CompletableFuture.completedFuture(-1);
        liftLog.info("控制继电器电梯输出关闭，开启手动模式发送记录：{},电梯号:{},发送指令时间:{}", ByteBufUtil.hexDump(buf),liftCode,LocalDateTime.now());
        ChannelFuture channelFuture = this.channel.writeAndFlush(buf);
        try {
            Thread.sleep(LiftLock.sleep);
            liftLog.info("控制继电器电梯输出关闭，开启手动模式睡眠10毫秒时间:{},电梯号:{}", LocalDateTime.now(),liftCode);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return commonHandle(channelFuture, 0x00);
    }
    private CompletableFuture<Integer> commonHandle(ChannelFuture channelFuture, int frame) {
        return CompletableFuture.supplyAsync(() -> {
            int count = 0;
            while (count < 3 && !channelFuture.isSuccess()) {
                count++;
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {

                }
            }

            if (channelFuture.isSuccess()) {
                return 1;
            } else {
                return 0;
            }
        })
                .thenApply((res) -> {
//                    int count = 0;
//                    int response = this.liftMapper.findResponse(this.liftCode, frame);
//                    while (count < 3 && response == -1) {
//                        count++;
//                        try {
//                            TimeUnit.SECONDS.sleep(1);
//                        } catch (InterruptedException e) {
//
//                        }
//                        response = this.liftMapper.findResponse(this.liftCode, frame);
//                    }
                    return 0;
                });
    }
}
